package z.json.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.json.JSONArray;
import org.json.JSONObject;

import z.json.annotation.ClassType;
import z.json.annotation.NowJson;
import z.json.annotation.OldJson;
import z.utils.LogUtils;
import framework.utils.StringUtils;

public class BeanUtilsNew {

	public static <T> ArrayList<T> getNowBeanArray(Class<T> clazz, String json) {
		try {
			return getNowBeanArray(clazz, new JSONArray(json));
		} catch (Exception e) {
			LogUtils.exception(e);
			return null;
		}
	}

	public static <T> ArrayList<T> getNowBeanArray(Class<T> clazz, JSONArray ja) {
		ArrayList<T> array = new ArrayList<T>();
		for (int i = 0; i < ja.length(); i++) {
			try {
				array.add(nowBean(clazz, ja.getJSONObject(i)));
			} catch (Exception e) {
				LogUtils.exception(e);
			}
		}
		return array;
	}

	public static <T> ArrayList<T> getOldBeanArray(Class<T> clazz, String json) {
		try {
			return getOldBeanArray(clazz, new JSONArray(json));
		} catch (Exception e) {
			LogUtils.exception(e);
			return null;
		}
	}

	public static <T> ArrayList<T> getOldBeanArray(Class<T> clazz, JSONArray ja) {
		ArrayList<T> array = new ArrayList<T>();
		for (int i = 0; i < ja.length(); i++) {
			try {
				array.add(oldBean(clazz, ja.getJSONObject(i)));
			} catch (Exception e) {
				LogUtils.exception(e);
			}
		}
		return array;
	}

	public static <T> T nowBean(Class<T> clazz, String json) {
		try {
			return nowBean(clazz, new JSONObject(json));
		} catch (Exception e) {
			LogUtils.exception(e);
			return null;
		}
	}

	public static <T> T nowBean(Class<T> clazz, JSONObject jsonObject) {
		return jsonObjectToBean(clazz, jsonObject, true);
	}

	public static <T> T oldBean(Class<T> clazz, String json) {
		try {
			return oldBean(clazz, new JSONObject(json));
		} catch (Exception e) {
			LogUtils.exception(e);
			return null;
		}
	}

	public static <T> T oldBean(Class<T> clazz, JSONObject jsonObject) {
		return jsonObjectToBean(clazz, jsonObject, false);
	}

	private static <T> T jsonObjectToBean(Class<T> clazz, JSONObject jsonObject, boolean isNow) {
		T newInstance = null;
		try {
			newInstance = clazz.newInstance();
		} catch (Exception e) {
			LogUtils.exception(e);
		}

		HashMap<String, String> names = new HashMap<String, String>();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (isNow) {
				NowJson annotation = field.getAnnotation(NowJson.class);
				if (annotation != null) {
					names.put(annotation.value(), field.getName());
				}
			} else {
				OldJson annotation = field.getAnnotation(OldJson.class);
				if (annotation != null) {
					names.put(annotation.value(), field.getName());
				}
			}
		}

		Iterator keys = jsonObject.keys();
		Field name = null;
		while (keys.hasNext()) {
			try {
				String key = (String) keys.next();
				String filedName = names.get(key);
				name = clazz.getDeclaredField(filedName == null ? key : filedName);
				name.setAccessible(true);
				String className = name.getType().getName().trim();
				if (isStringClass(className)) {
					name.set(newInstance, jsonObject.getString(key));
				} else if (isIntClass(className)) {
					name.setInt(newInstance, jsonObject.getInt(key));
				} else if (isLongClass(className)) {
					name.setLong(newInstance, jsonObject.getLong(key));
				} else if (isBooleanClass(className)) {
					name.setBoolean(newInstance, jsonObject.getBoolean(key));
				} else if (isArraylistClass(className)) {
					String genericsTypeName = name.getAnnotation(ClassType.class).classType().getName();
					JSONArray jsonArray = jsonObject.getJSONArray(key);
					ArrayList array = new ArrayList();
					if (isBaseDataType(genericsTypeName)) {
						for (int i = 0; i < jsonArray.length(); i++) {
							array.add(jsonArray.get(i));
						}
					} else {
						for (int i = 0; i < jsonArray.length(); i++) {
							array.add(jsonObjectToBean(Class.forName(genericsTypeName), jsonArray.getJSONObject(i), isNow));
						}
					}
					name.set(newInstance, array);
				} else {
					if (StringUtils.isNotBlank(jsonObject.get(key) + "")) {
						name.set(newInstance, jsonObjectToBean(Class.forName(className), jsonObject.getJSONObject(key), isNow));
					}
				}
			} catch (Exception e) {
				LogUtils.log("name:" + (name == null ? "" : name.getName()));
				LogUtils.exception(e);
			}
		}
		return newInstance;
	}

	public static String nowJsonArray(ArrayList list) {
		if (list != null && list.size() > 0) {
			JSONArray ja = new JSONArray();
			for (int i = 0; i < list.size(); i++) {
				ja.put(nowJsonObject(list.get(i)));
			}
			return ja.toString();
		} else {
			return "";
		}
	}

	public static String nowJson(Object obj) {
		return nowJsonObject(obj).toString();
	}

	public static JSONObject nowJsonObject(Object obj) {
		if (obj == null) {
			return null;
		}
		Class clazz = obj.getClass();
		JSONObject jo = new JSONObject();

		Field[] fields = clazz.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			try {

				Field field = fields[i];
				field.setAccessible(true);
				String className = field.getType().getName().trim();
				NowJson annotation = field.getAnnotation(NowJson.class);
				if (isStringClass(className)) {
					if (annotation != null) {
						jo.put(annotation.value(), field.get(obj));
					} else {
						jo.put(field.getName(), field.get(obj));
					}
				} else if (isIntClass(className)) {
					if (annotation != null) {
						jo.put(annotation.value(), field.getInt(obj));
					} else {
						jo.put(field.getName(), field.getInt(obj));
					}
				} else if (isLongClass(className)) {
					if (annotation != null) {
						jo.put(annotation.value(), field.getLong(obj));
					} else {
						jo.put(field.getName(), field.getLong(obj));
					}
				} else if (isBooleanClass(className)) {
					if (annotation != null) {
						jo.put(annotation.value(), field.getBoolean(obj));
					} else {
						jo.put(field.getName(), field.getBoolean(obj));
					}
				} else if (isArraylistClass(className)) {
					String genericsTypeName = field.getAnnotation(ClassType.class).classType().getName();
					JSONArray ja = new JSONArray();
					ArrayList array = (ArrayList) field.get(obj);

					if (isBaseDataType(genericsTypeName)) {
						for (int j = 0; j < array.size(); j++) {
							ja.put(array.get(j));
						}
					} else {
						for (int j = 0; j < array.size(); j++) {
							ja.put(nowJsonObject(array.get(j)));
						}
					}

					if (annotation != null) {
						jo.put(annotation.value(), ja);
					} else {
						jo.put(field.getName(), ja);
					}
				} else {
					if (annotation != null) {
						jo.put(annotation.value(), nowJsonObject(field.get(obj)));
					} else {
						jo.put(field.getName(), nowJsonObject(field.get(obj)));
					}
				}
			} catch (Exception e) {
				LogUtils.exception(e);
			}
		}
		return jo;
	}

	private static boolean isBaseDataType(String className) {
		if (isBooleanClass(className)) {
			return true;
		} else if (isIntClass(className)) {
			return true;
		} else if (isLongClass(className)) {
			return true;
		} else if (isStringClass(className)) {
			return true;
		} else if (isByteClass(className)) {
			return true;
		} else if (isCharClass(className)) {
			return true;
		} else if (isDoubleClass(className)) {
			return true;
		} else if (isFloatClass(className)) {
			return true;
		} else {
			return false;
		}
	}

	private static boolean isStringClass(String className) {
		return String.class.getName().equals(className);
	}

	private static boolean isIntClass(String className) {
		return "int".equals(className) || Integer.class.getName().equals(className);
	}

	private static boolean isLongClass(String className) {
		return "long".equals(className) || Long.class.getName().equals(className);
	}

	private static boolean isBooleanClass(String className) {
		return "boolean".equals(className) || Boolean.class.getName().equals(className);
	}

	private static boolean isByteClass(String className) {
		return "byte".equals(className) || Byte.class.getName().equals(className);
	}

	private static boolean isCharClass(String className) {
		return "char".equals(className) || Character.class.getName().equals(className);
	}

	private static boolean isDoubleClass(String className) {
		return "double".equals(className) || Double.class.getName().equals(className);
	}

	private static boolean isFloatClass(String className) {
		return "float".equals(className) || Float.class.getName().equals(className);
	}

	private static boolean isArraylistClass(String className) {
		return ArrayList.class.getName().equals(className);
	}

}
